#include <stddef.h>

/**
 * Determine whether pointer mangling (POINTER_GUARD) is needed:
 * Currently we assume this is necessary for GLIBC 2.4 and later
 */
#ifdef __GNU_LIBRARY__
#if __GLIBC_PREREQ(2, 4)
#define USE_POINTER_GUARD
#endif
#endif

#ifdef USE_POINTER_GUARD
#if defined(__linux__) && defined(__i386__)
/** taken from glibc */
typedef struct
{
    void *tcb;
    void *dtv;
    void *self;
    int multiple_threads;
    unsigned int sysinfo;
    unsigned int stack_guard;
    unsigned int pointer_guard;
} tcbhead_t;

#if __GLIBC_PREREQ(2, 6)
#define PTR_MANGLE(var)		asm ("xorl %%gs:%c2, %0\n"		      \
				     "roll $9, %0"			      \
				     : "=r" (var)			      \
				     : "0" (var),			      \
				       "i" (offsetof (tcbhead_t,	      \
						      pointer_guard)))
#define PTR_DEMANGLE(var)	asm ("rorl $9, %0\n"			      \
				     "xorl %%gs:%c2, %0"		      \
				     : "=r" (var)			      \
				     : "0" (var),			      \
				       "i" (offsetof (tcbhead_t,	      \
						      pointer_guard)))
#else
#define PTR_MANGLE(var)	asm ("xorl %%gs:%c2, %0"		      \
			     : "=r" (var)			      \
			     : "0" (var),			      \
			       "i" (offsetof (tcbhead_t,	      \
					      pointer_guard)))
#define PTR_DEMANGLE(var)	PTR_MANGLE (var)
#endif

#elif defined(__linux__) && defined(__x86_64__)

/** taken from glibc */
typedef struct
{
  void *tcb;
  void *dtv;
  void *self;
  int multiple_threads;
  uintptr_t sysinfo;
  uintptr_t stack_guard;
  uintptr_t pointer_guard;
} tcbhead_t;

#if __GLIBC_PREREQ(2, 6)
#define PTR_MANGLE(var)		asm ("xorq %%fs:%c2, %0\n"		      \
				     "rolq $17, %0"			      \
				     : "=r" (var)			      \
				     : "0" (var),			      \
				       "i" (offsetof (tcbhead_t,	      \
						      pointer_guard)))
#define PTR_DEMANGLE(var)	asm ("rorq $17, %0\n"			      \
				     "xorq %%fs:%c2, %0"		      \
				     : "=r" (var)			      \
				     : "0" (var),			      \
				       "i" (offsetof (tcbhead_t,	      \
						      pointer_guard)))
#else
#define PTR_MANGLE(var)	asm ("xorq %%fs:%c2, %0"			\
			     : "=r" (var)				\
			     : "0" (var),				\
			       "i" (offsetof (tcbhead_t,		\
					      pointer_guard)))
#define PTR_DEMANGLE(var)	PTR_MANGLE (var)
#endif

#elif defined(__linux__) && defined(__PPC__)

/** taken from glibc */
typedef struct
{
  uintptr_t pointer_guard;
  uintptr_t stack_guard;
  void *dtv;
} tcbhead_t;

# ifndef __powerpc64__
register void *__thread_register __asm__ ("r2");
# else
register void *__thread_register __asm__ ("r13");
# endif

# define TLS_TCB_OFFSET 0x7000

# define THREAD_GET_POINTER_GUARD() \
    (((tcbhead_t *) ((char *) __thread_register                               \
                     - TLS_TCB_OFFSET))[-1].pointer_guard)

#  define PTR_MANGLE(var) \
  (var) = (__typeof (var)) ((uintptr_t) (var) ^ THREAD_GET_POINTER_GUARD ())
#  define PTR_DEMANGLE(var)     PTR_MANGLE (var)

#else
#error POINTER_GUARD support not implemented for this architecture
#endif
#else
#define PTR_MANGLE(var)
#define PTR_DEMANGLE(var)
#endif

